package cn.csg.ams.collect2.poptopics.dataHandle;


import cn.csg.ams.collect2.poptopics.dao.MkPrPfOutageRecoveryMonDao;
import cn.csg.ams.collect2.poptopics.datahub.OutageJudge;
import cn.csg.ams.collect2.poptopics.datahub.TupleRecordDataUtils;
import cn.csg.ams.collect2.poptopics.entity.OutageJudgeData;
import cn.csg.ams.collect2.poptopics.entity.OutageJudgeVO;
import cn.csg.ams.collect2.poptopics.entity.RecordInfo;
import cn.csg.ams.collect2.poptopics.entity.bo.MkPrPfOutageRecoveryMonBO;
import cn.csg.ams.collect2.poptopics.entity.vo.MkPrPfOutageRecoveryMonVO;
import cn.csg.ams.collect2.poptopics.infrastructure.constant.UserCategoryConstant;
import cn.csg.ams.collect2.poptopics.infrastructure.utils.DateUtil;
import cn.csg.ams.collect2.poptopics.infrastructure.utils.RedisLockUtil;
import cn.csg.ams.collect2.poptopics.redisPool.AlarmPool;
import cn.csg.ams.collect2.poptopics.redisPool.OutagePool;
import cn.csg.ams.collect2.poptopics.service.MkMcTermAlarmLogService;
import cn.csg.ams.collect2.poptopics.service.MkPrPfOutageEventDetailService;
import cn.csg.ams.collect2.poptopics.service.MkPrPfOutageRecoveryMonService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.aliyun.datahub.client.model.TupleRecordData;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.security.SecureRandom;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.stream.Collectors;

/**
 * 停复电处理
 */
@Component
public class OutgaeAndRecover {

    private static Logger logger = LoggerFactory.getLogger(OutgaeAndRecover.class);

    @Autowired
    private OutageJudge outageJudge;

    @Autowired
    private OutagePool outagePool;

    @Autowired
    private AlarmPool alarmPool;


    @Autowired
    private MkPrPfOutageRecoveryMonDao outageRecoveryMonDao;

    @Autowired
    private MkPrPfOutageRecoveryMonService recoveryMonService;

    @Autowired
    private MkPrPfOutageEventDetailService outageEventDetailService;

    @Autowired
    private MkMcTermAlarmLogService alarmLogService;

    @Autowired
    private RedisLockUtil redisLockUtil;


    /**
     * 告警停电
     */
    public void alarmOutage(TupleRecordData data, RecordInfo recordInfo, long systemTime, long startTime) {
        /**
         * 1、根据告警信息 判断为  终端告警（台区总表）  分路告警（分路考核户）   低压用户告警（电表 低压居民用户）
         * 2、查询停电池 有没有对应的停电信息  台区、分路、低压用户    A 有  B 没有
         * 3、查询研判池 有没有对应的该条告警  A 有   B  没有
         *  A A  丢弃告警直接 return
         *  A B  将告警插入研判池子  retun
         *  B A  更新研判池内的告警 然后进行停电研判
         *  B B  将告警插入 研判池  然后进行停电研判
         */

        // 运行电能表标识
        // String runTermId = TupleRecordDataUtils.getStringField(data, 1);
        // 运行电能表标识
        String runMeterId = TupleRecordDataUtils.getStringField(data, 2);
        // 告警编码
        // String alarmCode = TupleRecordDataUtils.getStringField(data, 4);
        // // 告警来源，代码分类：GJLY=|{1终端；2电表；3主站}
        // String alarmSourCode = TupleRecordDataUtils.getStringField(data, 11);
        // // 终端类型
        // String termType = TupleRecordDataUtils.getStringField(data, 16);
        // 地区编码
        String areaCode = TupleRecordDataUtils.getStringField(data, "area_code");
        // 台区编号
        String taNo = recordInfo.getTaNo();
        // 当前对象停电告警时间
        String alarmTime = TupleRecordDataUtils.getStringField(data, 23);
        // 停电告警时间
        Date outageStartTime = DateUtil.parse(alarmTime, "yyyyMMddHHmmss");

        // 为了防止告警数据重复，导致输出数据重复，添加一步判断，去除重复结果的同时，过滤告警数据，加快研判速度
        String taOutageEndTimes = outagePool.getTaOutageEndTime(taNo, areaCode);
        Date taOutageEndTime = DateUtil.parse(taOutageEndTimes, "yyyyMMddHHmmss");
        // 停电告警 若 小于台区的复电时间，则认为该条停电告警数据无效（在台区复电后上报的 停电告警视为无效）
        if (taOutageEndTimes != null && outageStartTime.getTime() - taOutageEndTime.getTime() < 1000 * 60 * 3) {
            return;
        }

        // 查询停电池 有没有对应的停电信息
        OutagePool.OutageInfo outageInfo = outagePool.getOutageInfo(taNo, runMeterId, areaCode);

        // 查询研判池 有没有对应的该条告警
        AlarmPool.AlarmInfo alarmInfo = alarmPool.getTaAlarm(taNo, runMeterId, areaCode);

        // A A  丢弃告警直接 return
        if (outageInfo != null && alarmInfo != null) {
            return;
        }

        // A B  将告警插入研判池子  retun
        if (outageInfo != null && alarmInfo == null) {
            alarmPool.addAlarmToPool(data, recordInfo, areaCode);
            return;
        }

        // B A  更新研判池内的告警 然后进行停电研判
        if (outageInfo == null && alarmInfo != null) {
            alarmPool.addAlarmToPool(data, recordInfo, areaCode);
            // 进行停电研判
            outageJudge(data, recordInfo, systemTime, startTime);
            return;
        }

        // B B  将告警插入 研判池  然后进行停电研判
        if (outageInfo == null && alarmInfo == null) {
            alarmPool.addAlarmToPool(data, recordInfo, areaCode);
            // 进行停电研判
            outageJudge(data, recordInfo, systemTime, startTime);
        }
    }


    /**
     * 1、不满足研判规则 return
     * <p>
     * 2、台区停电
     * 2.1、 XX加锁XX  查询停电池  查看是否有对应的台区停电信息 有 XX释放锁XX return
     * 2.2、 停电池 没有台区停电信息  获取台区下所有的用户信息
     * a、在全量用户表当中查询 全台区用户信息   b、查询实时停复电表中对应用户信息  c、删除实时停复电表中对应用户信息 d、插入台区用户进入实时停复电表
     * 将对应信息插入停电池
     * XX释放锁XX  return
     * <p>
     * 3、分路停电
     * 3.1、 XX加锁XX  查询停电池  查看是否有对应的分路停电信息 有 XX释放锁XX return
     * 3.2、 停电池 没有分路停电信息  获取分路下所有的用户信息
     * a、在全量用户表当中查询 全分路用户信息   b、查询实时停复电表中对应用户信息  c、删除实时停复电表中对应用户信息 d、插入分路用户进入实时停复电表
     * 将对应信息插入停电池
     * XX释放锁XX  return
     * <p>
     * 4、部分用户停电
     * 4.1、 XX加锁XX  查询停电池  查看是否有对应的用户停电信息 全部都有 XX释放锁XX return
     * 4.2、 停电池 部分有  没有的用户使用已经存在的用户的停电事件id
     * a、在全量用户表当中查询 部分用户信息   b、查询实时停复电表中对应用户信息  c、删除实时停复电表中对应用户信息 d、插入用户进入实时停复电表
     * 将对应信息插入停电池
     * XX释放锁XX  return
     * 4.3 停电池 全没有  创建事件id
     * a、在全量用户表当中查询 部分用户信息   b、查询实时停复电表中对应用户信息  c、删除实时停复电表中对应用户信息 d、插入用户进入实时停复电表
     * 将对应信息插入停电池
     * XX释放锁XX  return
     */
    public void outageJudge(TupleRecordData data, RecordInfo recordInfo, long systemTime, long startTime) {
        // 地区编码
        String areaCode = TupleRecordDataUtils.getStringField(data, "area_code");
        // 当前对象停电告警时间
        String alarmTime = TupleRecordDataUtils.getStringField(data, 23);
        // 基准日期 （以告警时间为基准日期）
        Date baseTime = DateUtil.parse(alarmTime, "yyyyMMddHHmmss");

        // 研判参数
        OutageJudgeVO param = new OutageJudgeVO();
        param.setTaNo(recordInfo.getTaNo());
        param.setTaType(recordInfo.getTaType());
        param.setLowCustCnt(recordInfo.getLowCustCnt());

        // 获取该台区下所有告警信息, 进行停电研判
        List<AlarmPool.AlarmInfo> alarmList = alarmPool.getAlarmByTaNo(recordInfo.getTaNo(), areaCode);
        // 根据data的数据时间  往前推移3分钟  获取对应的告警数据  然后向下传递 进行停电研判
        alarmList = alarmList.stream().filter(alarmInfo -> checkTime(baseTime, alarmInfo.getAlarmTime())).collect(Collectors.toList());
        param.setAlarmList(alarmList);

        // 进行停电研判
        List<OutageJudgeData> judgeResultList = outageJudge.outageJudge(param);

        for (OutageJudgeData judgeResult : judgeResultList) {
            if (judgeResult.isStats()) {
                // 不满足研判规则 return
                afterJudgeSuccess(judgeResult, data, recordInfo, systemTime, startTime);
            }
        }
    }

    /**
     * 判断time是否在baseTime的3分钟内
     *
     * @param baseTime 基础时间
     * @param time     对象停电时间
     * @return boolean 大于3分钟，返回false， 3分钟内，返回true
     */
    private boolean checkTime(Date baseTime, String time) {
        if (time == null) {
            return false;
        }
        Date date = DateUtil.parse(time, "yyyyMMddHHmmss");
        return baseTime.getTime() - date.getTime() <= 60000 * 5;
    }


    /**
     * 研判成功后的操作
     */
    private void afterJudgeSuccess(OutageJudgeData judgeResult, TupleRecordData data, RecordInfo recordInfo, long systemTime, long startTime) {
        // 停电状态码 1 台区停电   2分路停电    0部分用户停电
        String judgeCode = judgeResult.getJudgeCode();
        // 地区编码
        String areaCode = TupleRecordDataUtils.getStringField(data, "area_code");

        // 加锁
        // String key = "fbs" + "_" + areaCode + "_" + recordInfo.getTaNo();
        // RLock lock = redissonClient.getLock(key);
        // lock.lock();

        String key = "fbs" + "_" + areaCode + "_" + recordInfo.getTaNo();
        String requestId = UUID.randomUUID().toString();
        while (!redisLockUtil.lock(key, requestId, 120 * 1000)) {
            // 未获取到锁，等待3秒后重试
            try {
                logger.info("等待锁。。。。 台区编号：" + recordInfo.getTaNo());
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }

        try {
            // 台区停电
            if ("1".equals(judgeCode)) {
                taOutage(judgeResult, data, recordInfo, systemTime, startTime);
            }

            // 分路停电
            if ("2".equals(judgeCode)) {
                branchOutage(judgeResult, data, recordInfo, systemTime, startTime);
            }

            // 部分用户停电
            if ("0".equals(judgeCode)) {
                partUserOutage(judgeResult, data, recordInfo, systemTime, startTime);
            }
        } finally {
            // 释放锁
            // lock.unlock();
            redisLockUtil.unlock(key, requestId);
        }
    }


    /**
     * 台区停电
     *
     * @param judgeResult 研判结果
     * @param data        告警数据
     * @param systemTime  告警进入datahub时间
     * @param startTime   研判接收告警时间
     */
    private void taOutage(OutageJudgeData judgeResult, TupleRecordData data, RecordInfo recordInfo, long systemTime, long startTime) {
        // 停电台区编号
        String taNo = judgeResult.getJudgeTa();
        // 地区编码
        String areaCode = TupleRecordDataUtils.getStringField(data, "area_code");
        // 告警时间
        String alarmTime = TupleRecordDataUtils.getStringField(data, 23);
        // 前置接收时间
        String preReceTime = TupleRecordDataUtils.getStringField(data, 22);
        // 满足的研判规则
        String judgeRule = judgeResult.getJudgeRule();
        // 停电事件id
        String eventId = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + (new SecureRandom().nextInt(90000) + 10000);

        // 为了防止告警数据重复，导致输出数据重复，添加一步判断，去除重复结果的同时，过滤告警数据，加快研判速度
        String taOutageEndTimes = outagePool.getTaOutageEndTime(taNo, areaCode);
        Date taOutageEndTime = DateUtil.parse(taOutageEndTimes, "yyyyMMddHHmmss");
        // 停电告警 若 小于台区的复电时间，则认为该条停电告警数据无效（在台区复电后上报的 停电告警视为无效）
        Date outageStartTime = DateUtil.parse(alarmTime, "yyyyMMddHHmmss");
        if (taOutageEndTimes != null && outageStartTime.getTime() - taOutageEndTime.getTime() < 1000 * 60 * 3) {
            return;
        }

        // 查询台区下的配变终端
        Map<String, String> pbTermAndMeter = alarmLogService.getPbTermAndMeterByTaNo(taNo, recordInfo.getTaType());
        String pbRunTermId = MapUtil.getStr(pbTermAndMeter, "mr_run_term_id");
        String pbRunMeterId = MapUtil.getStr(pbTermAndMeter, "mr_run_meter_id");
        data.setField(1, pbRunTermId);
        data.setField(2, pbRunMeterId);
        // RecordInfo taRecordInfo = alarmLogService.getRecordInfo(MapUtil.getStr(pbTermAndMeter, "mr_run_meter_id"), areaCode);
        // outageEventDetailService.saveOutageEvent(data, taRecordInfo, eventId, startTime, judgeRule);

        // 1.查询停电池  查看是否有对应的台区停电信息
        OutagePool.OutageInfo taOutageInfo = outagePool.getOutageInfo(taNo, pbRunMeterId, areaCode);
        if (taOutageInfo != null) {
            return;
        }

        // 2.停电池 没有台区停电信息  获取台区下所有的用户信息
        // 公变台区、分支、低压用户编号
        List<MkPrPfOutageRecoveryMonVO> list = null;
        List<String> userTypes = Arrays.asList(UserCategoryConstant.PUBLIC_TA, UserCategoryConstant.BRANCH, UserCategoryConstant.LOW_USER);
        if ("02".equals(recordInfo.getTaType())) {
            // 专变台区
            userTypes = Arrays.asList(UserCategoryConstant.SPECIALTY_TA, UserCategoryConstant.SPECIALTY_TA2);
            // 专变台区需要进行召测
            // boolean isTd = summonMeasure.summon(recordInfo.getTaNo(), areaCode);
            // if (!isTd) {
            //     // 若召测结果为未停电，则直接结束
            //     return;
            // }
            // 加上电能表标识，保证专变台区只输出一条数据
            list = outageRecoveryMonDao.queryList(areaCode, null, taNo, recordInfo.getRunMeterId(), null, userTypes);
        } else {
            list = outageRecoveryMonDao.queryList(areaCode, null, taNo, null, null, userTypes);
        }
        // 设置停电规则
        list.forEach(vo -> vo.setJudgeRule(judgeRule));

        // 匹配计划停电与故障停电
        // recoveryMonService.matchPlanAndFualtEvent(taNo, alarmTime, list);
        // 输出台区停电前，可能已经输出过台区下的部分用户停电，先删除台区下还在停电的用户
        outageRecoveryMonDao.delete(Wrappers.<MkPrPfOutageRecoveryMonBO>lambdaQuery()
                .eq(MkPrPfOutageRecoveryMonBO::getTaNo, taNo)
                .eq(MkPrPfOutageRecoveryMonBO::getOutageStatus, "1"));
        // 保存数据到 '实时停复电监测'
        recoveryMonService.saveRecoveryByLeafList(list, alarmTime, preReceTime, eventId, startTime, systemTime);
        // 将所有停电数据添加到停电池中
        outagePool.putOutageInfoToRedis(list, alarmTime, taNo, areaCode);

        // // 输出台区停电后，以线路编号为key，记录到redis中
        // outagePool.saveTaOutageToLine(recordInfo.getLineNo(), areaCode, recordInfo.getTaNo(), alarmTime);
        // // 判断是否输出线路停电
        // checkLineOutage(recordInfo.getLineNo(), areaCode, alarmTime);
    }

    private void checkLineOutage(String lineNo, String areaCode, String alarmTime) {
        // 台区停电时间
        Date baseTime = DateUtil.parse(alarmTime, "yyyyMMddHHmmss");

        // 查询线路下所有停电台区
        Map<Object, Object> taOutage = outagePool.getTaOutageByLine(lineNo, areaCode);
        // 判断线路是否已经停电，若已经停电，更新线路停电影响的台区、分路、用户数量
        if (taOutage.containsKey(lineNo)) {
            // TODO 更新线路影响的台区、分路、用户数量
            return;
        }

        // 判断5分钟内停电台区是否超过3个
        long count = taOutage.entrySet().stream().filter(entry -> {
            Date date = DateUtil.parse(entry.getValue().toString(), "yyyyMMddHHmmss");
            return baseTime.getTime() - date.getTime() <= 60000 * 5;
        }).count();

        if (count >= 3) {
            // 输出线路停电
            recoveryMonService.lineOutage(lineNo, alarmTime);
            // 记录线路停电
            outagePool.saveTaOutageToLine(lineNo, areaCode, lineNo, alarmTime);
        }

    }


    /**
     * 分路停电
     *
     * @param judgeResult 研判结果
     * @param data        告警数据
     * @param systemTime  告警进入datahub时间
     * @param startTime   研判接收告警时间
     */
    private void branchOutage(OutageJudgeData judgeResult, TupleRecordData data, RecordInfo recordInfo, long systemTime, long startTime) {

    }


    /**
     * 部分用户停电
     *
     * @param judgeResult 研判结果
     * @param data        告警数据
     * @param systemTime  告警进入datahub时间
     * @param startTime   研判接收告警时间
     */
    private void partUserOutage(OutageJudgeData judgeResult, TupleRecordData data, RecordInfo recordInfo, long systemTime, long startTime) {
        // 停电用户编号
        List<String> judgeMeter = judgeResult.getJudgeMeter();
        // 所属台区编号
        String taNo = judgeResult.getJudgeTa();

        // 地区编码
        String areaCode = TupleRecordDataUtils.getStringField(data, "area_code");
        // 告警时间
        String alarmTime = TupleRecordDataUtils.getStringField(data, 23);
        // 前置接收时间
        String preReceTime = TupleRecordDataUtils.getStringField(data, 22);
        // 满足的研判规则
        String judgeRule = judgeResult.getJudgeRule();

        // 从停电池中取出台区下所有停电信息
        List<OutagePool.OutageInfo> outageInfoList = outagePool.getOutageInfoByTaNo(taNo, areaCode);
        // 筛选出与研判结果重复的用户数据
        List<String> finalJudgeMeter = judgeMeter;
        outageInfoList = outageInfoList.stream().filter(outageInfo -> finalJudgeMeter.contains(outageInfo.getRunMeterId())).collect(Collectors.toList());
        List<String> repectRunMeterId = outageInfoList.stream().map(OutagePool.OutageInfo::getRunMeterId).collect(Collectors.toList());
        // 筛选出与研判结果不重复的数据
        judgeMeter = judgeMeter.stream().filter(runMeterId -> !repectRunMeterId.contains(runMeterId)).collect(Collectors.toList());
        if (CollUtil.isEmpty(judgeMeter)) {
            return;
        }

        // 停电事件id
        String eventId = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date()) + (new SecureRandom().nextInt(90000) + 10000);
        // 若已经存在部分用户的停电信息，则新加的用户也使用原来的用户的事件id
        if (CollUtil.isNotEmpty(outageInfoList)) {
            eventId = outageInfoList.get(0).getEventId();
        }

        // 生成当前节点的停电事件， 插入数据库
        outageEventDetailService.saveOutageEvent(data, recordInfo, eventId, startTime, judgeRule);
        // 查询部分停电的用户信息（此处的部分用户已经筛选了已经存在的重复数据）
        List<MkPrPfOutageRecoveryMonVO> list = outageRecoveryMonDao.queryList2(areaCode, judgeMeter);

        // 设置研判规则
        list.forEach(vo -> vo.setJudgeRule(judgeRule));
        // 匹配计划停电与故障停电
        // recoveryMonService.matchPlanAndFualtEvent(taNo, alarmTime, list);
        // 保存数据到 '实时停复电监测'
        recoveryMonService.saveRecoveryByLeafList(list, alarmTime, preReceTime, eventId, startTime, systemTime);
        // 将停电数据添加到停电池中
        outagePool.putOutageInfoToRedis(list, alarmTime, taNo, areaCode);
    }


    /**
     * 告警复电
     */
    public void alarmRecover(TupleRecordData data, RecordInfo recordInfo, long startTime) {
        // 地区编码
        String areaCode = recordInfo.getAreaCode();
        // 台区编号
        String taNo = recordInfo.getTaNo();
        // 运行电能表标识
        String runMeterId = recordInfo.getRunMeterId();

        // 告警来源，代码分类：GJLY=|{1终端；2电表；3主站}
        String alarmSourCode = TupleRecordDataUtils.getStringField(data, 11);
        // 复电告警时间(yyyyMMddHHmmss)
        String fdTime = TupleRecordDataUtils.getStringField(data, 23);

        // 删除告警池中的数据
        alarmPool.deleteTaAlarmInfo(taNo, runMeterId, areaCode);

        // 是否为线路考核户
        if (UserCategoryConstant.LINE.equals(recordInfo.getUserType())) {
            // 给线路复电
            lineRecover(recordInfo, fdTime, "1", startTime);
            return;
        }

        // 从停电池中查询是否存在该电表的停电信息 （此处复电告警若为集中器，查不出停电实体，无法复电; 区分终端电表的查询方式）
        OutagePool.OutageInfo outageInfo = null;
        if ("1".equals(alarmSourCode)) {
            // 若为终端复电告警，查询所属台区是否停电
            outageInfo = outagePool.getOutageInfoByTaNo(taNo, areaCode)
                    .stream().filter(o -> "2".equals(o.getObjectType()) || "3".equals(o.getObjectType()))
                    .findFirst().orElse(null);
        } else {
            outageInfo = outagePool.getOutageInfo(taNo, runMeterId, areaCode);
        }
        if (outageInfo == null) {
            return;
        }

        // 存在停电记录, 为该电表复电
        // 停电告警时间
        String tdTime = outageInfo.getAlarmTime();
        // 复电告警时间 < 停电告警时间， 丢弃
        if (DateUtil.parse(fdTime, "yyyyMMddHHmmss").getTime() < DateUtil.parse(tdTime, "yyyyMMddHHmmss").getTime()) {
            return;
        }

        // 若为复电，则修改数据库中该条记录， 并删除redis停电告警池中的数据
        updateAlarm(alarmSourCode, fdTime, recordInfo, "1", startTime);
    }


    /**
     * 线路复电
     *
     * @param recordInfo    档案信息
     * @param fdTime        复电时间
     * @param restoreSource 复电来源（1-告警流研判，2-告警批研判，3-计划工单，4-故障工单，5-电压采集）
     * @param startTime     复电接收时间
     */
    private void lineRecover(RecordInfo recordInfo, String fdTime, String restoreSource, long startTime) {
        // 地区编码
        String areaCode = recordInfo.getAreaCode();
        // 线路编号
        String lineNo = recordInfo.getLineNo();
        // 运行电能表标识
        String runMeterId = recordInfo.getRunMeterId();

        // 复电时间(yyyy-MM-dd HH:mm:ss)
        String outageEndTime = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(DateUtil.parse(fdTime, "yyyyMMddHHmmss"));

        // 查询线路是否停电
        String lineOutageStartTime = outagePool.getLineOutage(lineNo, areaCode);
        if (lineOutageStartTime == null) {
            return;
        }

        // 复电告警时间 < 停电告警时间， 丢弃
        if (DateUtil.parse(fdTime, "yyyyMMddHHmmss").getTime() <= DateUtil.parse(lineOutageStartTime, "yyyyMMddHHmmss").getTime()) {
            return;
        }

        // 线路对象单独复电
        outageRecoveryMonDao.recover(null, null, null, lineNo, outageEndTime, restoreSource, startTime);
        // 删除线路停电记录
        outagePool.deleteLineOutage(lineNo, lineNo, areaCode);
        // 删除set池中的记录
        outagePool.removeOutageSet(areaCode + "-" + runMeterId);
    }


    /**
     * 复电 (通过 电流电压，表码，功率等数据进行复电)
     *
     * @param outageEndTime 复电时间 (yyyyMMddHHmmss)
     * @param restoreSource 复电来源（1-告警流研判，2-告警批研判，3-计划工单，4-故障工单，5-电压采集, 6-表码， 7-功率）
     * @param recordInfo    档案信息
     * @param startTimes    接收数据时间
     */
    public void otherRecover(String outageEndTime, String restoreSource, RecordInfo recordInfo, long startTimes) {
        // 地区编码
        String areaCode = recordInfo.getAreaCode();
        // 运行电能表标识
        String runMeterId = recordInfo.getRunMeterId();

        // 是否为线路考核户
        if (UserCategoryConstant.LINE.equals(recordInfo.getUserType())) {
            // 给线路复电
            lineRecover(recordInfo, outageEndTime, restoreSource, startTimes);
            return;
        }

        // 删除告警池中的数据
        alarmPool.deleteTaAlarmInfo(recordInfo.getTaNo(), recordInfo.getRunMeterId(), areaCode);

        // 告警来源
        String alarmSourCode = "2";
        if ("04".equals(recordInfo.getTermType()) || "03".equals(recordInfo.getTermType())) {
            alarmSourCode = "1";
        }

        // 从停电池中查询是否存在该电表的停电信息
        OutagePool.OutageInfo outageInfo = outagePool.getOutageInfo(recordInfo.getTaNo(), runMeterId, areaCode);
        if (outageInfo == null) {
            // 若不存在，则丢弃该条复电数据
            return;
        }

        // 复电告警时间 < 停电告警时间， 丢弃
        // 停电开始时间
        String outageStartTime = outageInfo.getAlarmTime();
        if (DateUtil.parse(outageEndTime, "yyyyMMddHHmmss").getTime() < DateUtil.parse(outageStartTime, "yyyyMMddHHmmss").getTime()) {
            return;
        }
        // 若存在，则修改数据库中该条记录， 删除redis停电告警池中的数据
        updateAlarm(alarmSourCode, outageEndTime, recordInfo, restoreSource, startTimes);
    }


    /**
     * 复电
     *
     * @param alarmSourCode 告警来源（1-终端，2-电表）
     * @param outageEndTime 停电结束时间 (yyyyMMddHHmmss)
     * @param recordInfo    档案信息
     * @param restoreSource 复电来源（1-告警流研判，2-告警批研判，3-计划工单，4-故障工单，5-电压采集）
     */
    public void updateAlarm(String alarmSourCode, String outageEndTime, RecordInfo recordInfo, String restoreSource, Long startTime) {
        // 运行电能表标识
        String runMeterId = recordInfo.getRunMeterId();
        // 地区编码
        String areaCode = recordInfo.getAreaCode();
        // 所属台区编号
        String taNo = recordInfo.getTaNo();
        // 所属分路编号
        String branchNo = recordInfo.getBranchNo();

        // 若告警来源为终端, 为台区复电
        if ("1".equals(alarmSourCode)) {
            // 台区复电
            taReCover(outageEndTime, recordInfo, restoreSource, startTime);
        } else {
            // 电表复电
            // 修改数据库记录
            outageEventDetailService.updateOutageEvent(recordInfo, outageEndTime, null, null, restoreSource, startTime);
            // 删除停电池中的单条数据
            outagePool.deleteOutageInfo(taNo, runMeterId, areaCode);
        }


        // // 从停电池中取出台区下所有停电信息
        // List<OutagePool.OutageInfo> outageInfoList = outagePool.getOutageInfoByTaNo(recordInfo.getTaNo(), areaCode);
        // // 获取台区下停电的根节点
        // Set<String> objTypeSet = outageInfoList.stream().map(OutagePool.OutageInfo::getObjectType).collect(Collectors.toSet());
        //
        // // 研判参数
        // OutageJudgeVO param = new OutageJudgeVO();
        // // 获取该台区下所有告警信息, 进行复电研判
        // List<AlarmPool.AlarmInfo> alarmList = alarmPool.getAlarmByTaNo(recordInfo.getTaNo(), areaCode);
        // param.setAlarmList(alarmList);
        // param.setTaNo(taNo);
        // param.setTaType(recordInfo.getTaType());
        // param.setLowCustCnt(recordInfo.getLowCustCnt());
        // List<String> runMeterIds = alarmList.stream().map(AlarmPool.AlarmInfo::getRunMeterId).collect(Collectors.toList());
        //
        // // 停电的根节点为台区
        // if (objTypeSet.contains("2") || objTypeSet.contains("3")) {
        //     // 研判台区是否复电
        //     if (outageJudge.taOutageRestoreJudge(param)) {
        //         // 修改台区下所有记录
        //         outageEventDetailService.updateOutageEvent(recordInfo, outageEndTime, taNo, null, restoreType, startTime);
        //         // 删除停电池中整个台区的数据
        //         outagePool.deleteTaOutageInfo(taNo, areaCode);
        //         // 删除告警池中整个台区的数据
        //         alarmPool.deleteTaAlarmInfo(taNo, areaCode);
        //     }
        //     return;
        // }
        //
        // // 停电的根节点为分路
        // if (objTypeSet.contains("4")) {
        //     // 研判分路是否复电
        //     param.setShuntNo(branchNo);
        //     if (outageJudge.shuntOutageRestoreJudge(param)) {
        //         // 分路下所有电表
        //         List<String> branchRunMeterIds = alarmList.stream().filter(alarmInfo -> Objects.equals(branchNo, alarmInfo.getShuntNo()))
        //                 .map(AlarmPool.AlarmInfo::getRunMeterId)
        //                 .collect(Collectors.toList());
        //         // 修改分路下所有记录
        //         outageEventDetailService.updateOutageEvent(recordInfo, outageEndTime, taNo, branchRunMeterIds, restoreType, startTime);
        //         // 删除停电池中分路的数据
        //         outagePool.deleteTaOutageInfo(taNo, branchRunMeterIds, areaCode);
        //         // 删除告警池中分路的数据
        //         alarmPool.deleteTaAlarmInfo(taNo, branchRunMeterIds, areaCode);
        //     }
        //     return;
        // }
        //
        // // 停电的根节点为用户
        // if (objTypeSet.contains("5")) {
        //     // 研判部分用户是否全部复电
        //     if (outageJudge.meterOutageRestoreJudge(param)) {
        //         // 修改所有用户的停电记录
        //         outageEventDetailService.updateOutageEvent(recordInfo, outageEndTime, taNo, runMeterIds, restoreType, startTime);
        //         // 删除停电池中所有用户的数据
        //         outagePool.deleteTaOutageInfo(taNo, runMeterIds, areaCode);
        //         // 删除告警池中所有用户的数据
        //         alarmPool.deleteTaAlarmInfo(taNo, runMeterIds, areaCode);
        //     }
        // }

    }


    /**
     * 给台区复电
     */
    public void taReCover(String outageEndTime, RecordInfo recordInfo, String restoreSource, Long startTime) {
        // 地区编码
        String areaCode = recordInfo.getAreaCode();
        // 台区编号
        String taNo = recordInfo.getTaNo();

        // 修改台区下所有记录
        outageEventDetailService.updateOutageEvent(recordInfo, outageEndTime, taNo, null, restoreSource, startTime);
        // 删除停电池中整个台区的数据
        outagePool.deleteTaOutageInfo(taNo, areaCode);
        // 删除告警池中整个台区的数据
        alarmPool.deleteTaAlarmInfo(taNo, areaCode);
        // 记录台区的复电时间
        outagePool.setTaOutageEndTime(taNo, areaCode, outageEndTime);
    }


}
